using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Datorgrafik_lab1.Components;
using Datorgrafik_lab1.Objects;
using Datorgrafik_lab1.Models;
using Datorgrafik_lab1.Helpers;
using Datorgrafik_lab1.Objects.Airplane;
using Datorgrafik_lab1.Components.Input;

namespace Datorgrafik_lab1 {
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game {
        private GraphicsDeviceManager graphics;
        private GraphicsDevice device;
        private SpriteBatch spriteBatch;

        private Camera camera;

        Axes axes;
        City newcity;
        Airplane airplane;
        Ground newterrain;

        private int windowWidth;
        private int windowHeight;

        //--
        private BasicEffect effect;
        RasterizerState WIREFRAME_RASTERIZER_STATE = new RasterizerState() { CullMode = CullMode.CullCounterClockwiseFace, FillMode = FillMode.Solid };
        //--


        public Game1() {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            IsFixedTimeStep = false;

            //Add components
            Components.Add(new FPS(this));
            InputHandler ip = new InputHandler(this);
            Components.Add(ip);
            Services.AddService(typeof(IInputHandler), ip);
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize() {
            base.Initialize();

            // Setup the window to be a quarter the size of the desktop.
            windowWidth = graphics.GraphicsDevice.DisplayMode.Width / 2;
            windowHeight = graphics.GraphicsDevice.DisplayMode.Height / 2;

            // Setup frame buffer.
            graphics.SynchronizeWithVerticalRetrace = false;
            graphics.PreferredBackBufferWidth = windowWidth;
            graphics.PreferredBackBufferHeight = windowHeight;
            graphics.PreferMultiSampling = true;
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            device = graphics.GraphicsDevice;
            this.camera = new Camera(device);

            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(device);

            //---
            GraphicsDevice.RasterizerState = WIREFRAME_RASTERIZER_STATE;
            effect = new BasicEffect(graphics.GraphicsDevice);
            effect.VertexColorEnabled = true;
            effect.Projection = camera.ViewProjectionMatrix;
            //---
       
            newterrain = new Ground(device, new Vector3(0,-5,0),1f);
            axes = new Axes(device);
            newcity = new City(device, new Vector3(0, -5, 0), 1f);
            airplane = new Airplane(device, Vector3.Zero, 0.5f);
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent() {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime) {
            float elapsedTime = (float)gameTime.ElapsedGameTime.Milliseconds;

            //Read input
            IInputHandler inputHandler = (IInputHandler)Services.GetService(typeof(IInputHandler));
            inputAction(inputHandler.getUnhandledActions(), elapsedTime);

            //To make the camera mov
            camera.Update(airplane);

            airplane.Update(elapsedTime);

            base.Update(gameTime);
        }

        private void inputAction(List<ActionType> actions, float elapsedTime) {
            foreach(var action in actions) {
                if(action == ActionType.Quit)
                    this.Exit();
                airplane.PerformAction(action, elapsedTime);
            }
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime) {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            //Switch to 3D rendering since we have drawn the FPS in 2D
            GraphicsDevice.BlendState = BlendState.Opaque;
            GraphicsDevice.DepthStencilState = DepthStencilState.Default;            
            effect.View = camera.ViewMatrix;
            effect.World = Matrix.Identity;
            effect.CurrentTechnique.Passes[0].Apply();

            //Draw 3D object
            
            //axes.draw();

            Matrix identity = Matrix.Identity;

            newterrain.Draw(ref effect, ref identity);

            newcity.Draw(ref effect, ref identity);

            airplane.Draw(ref effect, ref identity);


            base.Draw(gameTime);
        }
    }
}
